package ph.com.gs3.formalistics.view.documentview;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import ph.com.gs3.formalistics.global.constants.GlobalConstants;
import ph.com.gs3.formalistics.model.valueobjects.business.FieldProperties;
import ph.com.gs3.formalistics.model.valueobjects.business.form.FormField;
import ph.com.gs3.formalistics.view.custom.fields.AttachmentField;
import ph.com.gs3.formalistics.view.custom.fields.BarcodeScannerField;
import ph.com.gs3.formalistics.view.custom.fields.DateTimePickerField;
import ph.com.gs3.formalistics.view.custom.fields.BarcodeScannerField.BarcodeScannerListener;
import ph.com.gs3.formalistics.view.custom.fields.DateTimePickerField.DateTimePickerListener;
import ph.com.gs3.formalistics.view.custom.fields.DateTimePickerField.PickerType;
import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;

public class FieldBuilder {

	public static final String TAG = FieldBuilder.class.getSimpleName();

	public static final int groupViewsRightPadding = 19;

	private Context context;
	private String currentServer;

	private LayoutParams lpFillWidth;

	private GlobalConstants constants = GlobalConstants.getApplicationInstance();

	// Field Listeners
	private DateTimePickerListener dateTimePickerListener;
	private BarcodeScannerListener barcodeScannerListener;

	public static enum EditTextType {
		MULTILINE, NORMAL
	}

	public FieldBuilder(Context context, String currentServer) {
		this.context = context;
		this.currentServer = currentServer;

		lpFillWidth = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
		        LinearLayout.LayoutParams.WRAP_CONTENT);

	}

	public void setSpecialFieldsListener(Activity activity) {

		String exceptionMessage = "The activity " + activity.getClass().getSimpleName()
		        + " must implement ";

		try {
			this.dateTimePickerListener = (DateTimePickerListener) activity;
		} catch (ClassCastException e) {
			throw new IllegalStateException(exceptionMessage + " DateTimePickerListener");
		}

		try {
			this.barcodeScannerListener = (BarcodeScannerListener) activity;
		} catch (ClassCastException e) {
			throw new IllegalStateException(exceptionMessage + " BarcodeScannerListener");
		}

	}

	public View createField(FormField formField, FieldProperties properties) {

		View fieldView = null;

		switch (formField.getType()) {
			case TEXT_FIELD:
				fieldView = createEditText(formField, EditTextType.NORMAL, properties.isEnabled());
				break;
			case TEXT_AREA:
				fieldView = createEditText(formField, EditTextType.MULTILINE,
				        properties.isEnabled());
				break;
			case CHECK_BOX_GROUP:
			case SELECT_MANY:
				fieldView = createCheckBoxGroup(formField, properties.isEnabled());
				break;
			case RADIO_BUTTON_GROUP:
				fieldView = createRadioButtonGroup(formField, properties.isEnabled());
				break;
			case DROPDOWN:
				fieldView = createSpinner(formField, properties.isEnabled());
				break;
			case DATE_PICKER:

				// code level validation
				if (dateTimePickerListener == null) {
					throw new IllegalStateException(
					        "Field builder cannot create date pickers without a date time picker listener");
				}

				fieldView = createDatePicker(formField, dateTimePickerListener, PickerType.DATE,
				        properties.isEnabled());
				break;
			case DATE_TIME_PICKER:
				// code level validation
				if (dateTimePickerListener == null) {
					throw new IllegalStateException(
					        "Field builder cannot create date pickers without a date time picker listener");
				}

				fieldView = createDatePicker(formField, dateTimePickerListener,
				        PickerType.DATETIME, properties.isEnabled());
				break;
			case TIME_PICKER:
				// code level validation
				if (dateTimePickerListener == null) {
					throw new IllegalStateException(
					        "Field builder cannot create date pickers without a date time picker listener");
				}

				fieldView = createDatePicker(formField, dateTimePickerListener, PickerType.TIME,
				        properties.isEnabled());
				break;
			case BARCODE_SCANNER:
				fieldView = createBarcodeScanner(formField, barcodeScannerListener,
				        properties.isEnabled());
				break;
			case SINGLE_ATTACHMENT:
				fieldView = createSingleAttachment(formField, properties.isEnabled());
				break;
			default:
				fieldView = null;

		}

		if (fieldView != null) {
			// set properties
			int visibility = properties.isHidden() ? View.GONE : View.VISIBLE;
			fieldView.setVisibility(visibility);
		}

		return fieldView;
	}

	// =============================================================================
	// {{ Field Builders

	public View createEditText(FormField formField, EditTextType type, boolean isEnabled) {

		LinearLayout container = createLabeledDefaultContainer(formField.getLabel(),
		        formField.getId() + "_container");

		EditText etField = new EditText(context);
		etField.setLayoutParams(lpFillWidth);
		etField.setTag(formField.getId());

		etField.setTypeface(constants.getGlobalTypeface());

		if (type == EditTextType.MULTILINE) {
			etField.setSingleLine(false);
			etField.setImeOptions(EditorInfo.IME_FLAG_NO_ENTER_ACTION);
		} else {
			etField.setSingleLine(true);
		}

		if (formField.getPlaceHolder() != null) {
			etField.setHint(formField.getPlaceHolder());
		}

		if (formField.getValue() != null) {
			etField.setText(formField.getValue());
		}

		// set enabled/disabled
		etField.setEnabled(isEnabled);
		container.addView(etField);

		return container;

	}

	public View createCheckBoxGroup(FormField formField, boolean isEnabled) {

		validateFieldsWithOptions(formField);

		LinearLayout container = createLabeledDefaultContainer(formField.getLabel(),
		        formField.getId() + "_container");

		// initialize checkbox data
		List<String> options = formField.getOptions();
		List<String> selectedOptions = new ArrayList<String>();

		if (formField.getValue() != null) {
			// TODO: replace hardcoded separator later
			String[] selectedOptionsRaw = formField.getValue().split("\\|\\^\\|");
			selectedOptions = new ArrayList<String>(Arrays.asList(selectedOptionsRaw));
		} else {
			selectedOptions = new ArrayList<String>();
		}

		// initialize checkbox view
		LinearLayout checkBoxContainer = new LinearLayout(context);

		checkBoxContainer.setPadding(0, 0, groupViewsRightPadding, 0);
		checkBoxContainer.setOrientation(LinearLayout.VERTICAL);
		checkBoxContainer.setTag(formField.getId());

		for (int i = 0; i < options.size(); i++) {

			String optionString = options.get(i);

			CheckBox cbField = new CheckBox(context);
			cbField.setTag(formField.getId() + "_" + i);

			cbField.setTypeface(constants.getGlobalTypeface());

			// check if the option is a value label pair, if it is, set the label as the
			// text,
			if (optionString.contains("|")) {
				cbField.setText(optionString.split("\\|")[1]);
			} else {
				cbField.setText(optionString);
			}

			// check each option that is selected
			if (selectedOptions != null) {
				for (String selectedOption : selectedOptions) {

					if (optionString.contains("|")) {
						String optionValue = optionString.split("\\|")[0];
						if (optionValue.equals(selectedOption.trim())) {
							cbField.setChecked(true);
						}
					} else {
						if (optionString.equals(selectedOption.trim())) {
							cbField.setChecked(true);
						}
					}

				}
			}

			cbField.setEnabled(isEnabled);
			checkBoxContainer.addView(cbField);

		}

		container.addView(checkBoxContainer);

		return container;

	}

	public View createRadioButtonGroup(FormField formField, boolean isEnabled) {

		validateFieldsWithOptions(formField);

		LinearLayout container = createLabeledDefaultContainer(formField.getLabel(),
		        formField.getId() + "_container");

		// initialize data
		List<String> options = formField.getOptions();
		String selectedValue = null;
		String selectedValueRaw = formField.getValue();

		if (selectedValueRaw != null) {
			if (selectedValueRaw.contains("|")) {
				selectedValue = selectedValueRaw.split("\\|")[0];
			} else {
				selectedValue = selectedValueRaw;
			}
		}

		// initialize the view
		RadioGroup radioGroup = new RadioGroup(context);

		radioGroup.setOrientation(RadioGroup.VERTICAL);
		radioGroup.setTag(formField.getId());
		radioGroup.setPadding(0, 0, groupViewsRightPadding, 0);

		boolean hasCheckedButton = false;
		int checkedButtonId = -1;

		for (int i = 0; i < options.size(); i++) {

			String optionString = options.get(i).trim();
			String optionLabel = optionString;
			String optionStringValue = optionString;

			RadioButton radioButton = new RadioButton(context);
			radioButton.setLayoutParams(lpFillWidth);

			radioButton.setId(i);
			radioButton.setTypeface(constants.getGlobalTypeface());

			// check if the option is value label pair, if it is, divide the option string
			// with | and assign the label and value properly
			if (optionString.contains("|")) {
				String[] splittedOptionString = optionString.split("\\|");

				optionStringValue = splittedOptionString[0];
				optionLabel = splittedOptionString[1];
			}
			radioButton.setText(optionLabel);

			if (selectedValue != null) {
				if (selectedValue.equals(optionStringValue)) {
					hasCheckedButton = true;
					checkedButtonId = radioButton.getId();
				}
			}

			radioButton.setEnabled(isEnabled);
			radioGroup.addView(radioButton);

		}

		container.addView(radioGroup);

		if (hasCheckedButton) {
			radioGroup.check(checkedButtonId);
		}

		return container;

	}

	public View createSpinner(FormField formField, boolean isEnabled) {

		LinearLayout container = createLabeledDefaultContainer(formField.getLabel(),
		        formField.getId() + "_container");

		Spinner spField = new Spinner(context);
		List<String> options = formField.getOptions();

		ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(context,
		        android.R.layout.simple_spinner_item, options);

		spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		spField.setAdapter(spinnerAdapter);

		String value = formField.getValue();

		// assign the value if this field is for viewing
		if (value != null && !"null".equals(value)) {
			if (options.indexOf(value) > -1) {
				spField.setSelection(options.indexOf(value));
			}
		}

		spField.setTag(formField.getId());
		spField.setEnabled(isEnabled);

		container.addView(spField);

		return container;

	}

	private View createDatePicker(FormField formField, DateTimePickerListener listener,
	        PickerType pickerType, boolean isEnabled) {

		DateTimePickerField dpField = new DateTimePickerField(context, pickerType);
		dpField.setId(formField.getId());
		dpField.setLabel(formField.getLabel());
		dpField.setValue(formField.getValue());
		dpField.setPlaceHolder(formField.getPlaceHolder());

		dpField.setEnabled(isEnabled);

		dpField.setListener(listener);

		return dpField;

	}

	private View createBarcodeScanner(FormField formField, BarcodeScannerListener listener,
	        boolean isEnabled) {

		BarcodeScannerField bsField = new BarcodeScannerField(context);
		bsField.setFieldId(formField.getId());
		bsField.setLabel(formField.getLabel());
		bsField.setScannedCode(formField.getValue());
		bsField.setListener(listener);

		return bsField;

	}

	private View createSingleAttachment(FormField formField, boolean isEnabled) {

		AttachmentField afield = new AttachmentField(context);
		afield.setFieldId(formField.getId());
		afield.setLabel(formField.getLabel());

		if (formField.getValue() != null && !formField.getValue().trim().isEmpty()) {
			afield.setAttachmentLink(currentServer + formField.getValue());
		}

		return afield;

	}

	// }}

	// =============================================================================
	// {{ Validators

	private void validateFieldsWithOptions(FormField formField) {

		List<String> options = formField.getOptions();

		if (options == null) {
			throw new IllegalStateException("This field (" + formField.getId()
			        + ") must have an options property. It cannot be null.");
		} else if (options.size() <= 0) {
			throw new IllegalStateException("This field (" + formField.getId()
			        + ") must have a non empty options property.");
		}

	}

	// }}

	// =============================================================================
	// {{ Container Builders

	private LinearLayout createLabeledDefaultContainer(String label, String tag) {

		LinearLayout container = createDefaultContainer(tag);

		TextView tvLabel = new TextView(context);
		tvLabel.setText(label);
		tvLabel.setTypeface(constants.getGlobalTypeface());

		container.addView(tvLabel);

		return container;

	}

	private LinearLayout createDefaultContainer(String tag) {

		LinearLayout container = new LinearLayout(context);

		container.setTag(tag);
		container.setLayoutParams(lpFillWidth);
		container.setOrientation(LinearLayout.VERTICAL);
		return container;

	}

	// }}

}
